import java.util.ArrayList;

/*
* Самопальная реализация кортежа.
* Реализована на максимально убогом принципе, потому требует
*   особого отношения ради сохранения инкапсуляции.
* >Инициализация проводится с помощью массива с SimpleNames
*   классов, которые должны будут содержаться.
* >Любая распаковка кортежа требует ввода названия распакуемого
*   класса в формате String. Несовпадение приводит к checked
*   исключению.
*/
public class Tuple {
    private final ArrayList<Object> body = new ArrayList<>();
    private final ArrayList<String> types = new ArrayList<>();

    Tuple(String objectClass) {
        body.add(objectClass);
        types.add(objectClass.getClass().toString());
    }
    Tuple(String[] classes) {
        for (String str : classes) {
            types.add(str);
            body.add(null);
        }
    }

    //Все проверки должны быть private, потому что
    //  предполагается, что классы, инкапсулированные в
    //  Tuple, статически заданы, а потому любое несоответствие
    //  - случай исключительный, требующий внимания при дебаге
    private boolean checkRequirements(String[] requirements) {
        if (requirements.length != types.size()) return false;
        for(int i = 0; i < types.size(); ++i) {
            if (!types.get(i).equals(requirements[i]))
                return false;
        }
        return true;
    }
    private void check(String[] requirements) throws Exception{
        if (!checkRequirements(requirements)) {
            StringBuilder get = new StringBuilder();
            for (String str : requirements) {
                get.append(str);
                get.append(", ");
            }
            throw new Exception(String.format("Invalid requirements:%n" +
                    "%s get, %s expected%n", get,
                    types));
        }
    }
    private void check(int index, String requirement) throws Exception {
        if (index >= body.size()) {
            throw (new Exception("Invalid Index"));
        }
        if (!types.get(index).equals(requirement)) {
            String get = types.get(index);
            throw (new Exception(String.format("Invalid requirement:%n" +
                    "%s get, %s expected%n", requirement, get)));
        }
    }
    public Object[] unpack(String[] requirements) throws Exception{
        check(requirements);
        return body.toArray();
    }
    public void set(int index, Object obj) throws ArrayStoreException {
        if (!types.get(index).equals(obj.getClass().getSimpleName())) {
            String objectClass = obj.getClass().getSimpleName();
            String systemType = types.get(index);
            System.err.printf("Error: %s expected, %s get.%n", objectClass, systemType);
            throw(new ArrayStoreException());
        }
        body.set(index, obj);
    }
    public void set(Object[] objects) {
        if (body.size() != objects.length) {
            System.err.printf("Error: body size is %d, but " +
                    "objects size is %d",body.size(),objects.length);
            throw(new ArrayIndexOutOfBoundsException());
        }
        for (int i = 0; i < objects.length; ++i) {
            set(i, objects[i]);
        }
    }
    public Object unpack(int index, String className) throws Exception {
        check(index, className);
        return body.get(index);
    }
    public int size() {
        return body.size();
    }
}
